const v = require('./ConstValue');
const { CellModel } = require('./CellModel');

class GameModel {
    constructor() {
        this.type = null;
        this.cells = null;
        this.cellBgs = null;
        this.lastPos = cc.v2(-1, -1);
        this.cellTypeNum = 5;
        this.cellCreateType = []; // 升成种类只在这个数组里面查找
    }

    init(cellTypeNum) {
        this.cells = [];
        this.setCellTypeNum(cellTypeNum || this.cellTypeNum);
        for (let i = 1; i <= v.GRID_ROWS; i++) {
            this.cells[i] = [];
            for (let j = 1; j <= v.GRID_COLUMNS; j++) {
                this.cells[i][j] = new CellModel();
            }
        }

        for (let i = 1; i <= v.GRID_ROWS; i++) {
            for (let j = 1; j <= v.GRID_COLUMNS; j++) {
                let flag = true; // 控制连续的动物不超过3个
                while (flag) {
                    flag = false;
                    this.cells[i][j].init(this.getRandomCellType());
                    let result = this.checkPoint(i, j)[0];
                    if (result.length > 2) {
                        flag = true;
                    }
                    this.cells[i][j].setXY(i, j);
                    this.cells[i][j].setStartXY(i, j);
                }
            }
        }

    }

    // 检查横纵是否有连续三个相同的元素
    checkPoint(x, y) {
        let checkWithDirection = function (x, y, direction) {
            let queue = [];
            let visited = [];
            visited[x * v.GRID_ROWS + y] = true;
            queue.push(cc.v2(x, y));
            let next = 0;
            while (next < queue.length) {
                let point = queue[next];
                next++;
                let cellModel = this.cells[point.x][point.y];
                if (!cellModel) {
                    // 当前没有格子
                    continue;
                }
                for (let i = 0; i < direction.length; i++) {
                    let tmpX = point.x + direction[i].x;
                    let tmpY = point.y + direction[i].y;
                    if (tmpX < 1 || tmpX > v.GRID_ROWS
                        || tmpY < 1 || tmpY > v.GRID_COLUMNS
                        || visited[tmpX * v.GRID_ROWS + tmpY]
                        || !this.cells[tmpX][tmpY]) {
                        continue; // 检查到边界则跳过
                    }
                    if (cellModel.type == this.cells[tmpX][tmpY].type) {
                        visited[tmpX * v.GRID_ROWS + tmpY] = true;
                        queue.push(cc.v2(tmpX, tmpY));
                    }
                }
            }
            return queue;
        }.bind(this);
        let rowResult = checkWithDirection.call(this, x, y, [cc.v2(0, 1), cc.v2(0, -1)]);
        let colResult = checkWithDirection.call(this, x, y, [cc.v2(1, 0), cc.v2(-1, 0)]);
        let result = [];
        let newCellStatus = "";
        if (rowResult.length >= 5 || colResult.length >= 5) {
            newCellStatus = v.CELL_STATUS.BIRD;
        }
        else if (rowResult.length >= 3 && colResult.length >= 3) {
            newCellStatus = v.CELL_STATUS.WRAP;
        }
        else if (rowResult.length >= 4) {
            newCellStatus = v.CELL_STATUS.LINE;
        }
        else if (colResult.length >= 4) {
            newCellStatus = v.CELL_STATUS.COLUMN;
        }
        if (rowResult.length >= 3) {
            result = rowResult;
        }
        if (colResult.length >= 3) {
            let tmp = result.concat();
            colResult.forEach(function (newElem) {
                let isAdd = false;
                tmp.forEach(function (oldElem) {
                    if (newElem.x == oldElem.x && newElem.y == oldElem.y) {
                        isAdd = true;
                    }
                }, this);
                if (!isAdd) {
                    result.push(newElem);
                }
            }, this);
        }
        return [result, newCellStatus, this.cells[x][y].type];
    }

    printInfo() {
        for (var i = 1; i <= 9; i++) {
            var printStr = "";
            for (var j = 1; j <= 9; j++) {
                printStr += this.cells[i][j].type + " ";
            }
            console.log(printStr);
        }
    }

    getCells() {
        return this.cells;
    }

    // controller调用的主要入口
    // 点击某个格子
    selectCell(pos) {
        this.changeModels = [];// 发生改变的model，将作为返回值，给view播动作
        this.effectsQueue = []; // 动物消失，爆炸等特效
        var lastPos = this.lastPos;
        var delta = Math.abs(pos.x - lastPos.x) + Math.abs(pos.y - lastPos.y);
        if (delta != 1) { //非相邻格子， 直接返回
            this.lastPos = pos;
            return [[], []];
        }
        let curClickCell = this.cells[pos.x][pos.y]; //当前点击的格子
        let lastClickCell = this.cells[lastPos.x][lastPos.y]; // 上一次点击的格子
        this.exchangeCell(lastPos, pos);
        var result1 = this.checkPoint(pos.x, pos.y)[0];
        var result2 = this.checkPoint(lastPos.x, lastPos.y)[0];
        this.curTime = 0; // 动画播放的当前时间
        this.pushToChangeModels(curClickCell);
        this.pushToChangeModels(lastClickCell);
        let isCanBomb = (curClickCell.status != v.CELL_STATUS.COMMON && // 判断两个是否是特殊的动物
            lastClickCell.status != v.CELL_STATUS.COMMON) ||
            curClickCell.status == v.CELL_STATUS.BIRD ||
            lastClickCell.status == v.CELL_STATUS.BIRD;
        if (result1.length < 3 && result2.length < 3 && !isCanBomb) {//不会发生消除的情况
            this.exchangeCell(lastPos, pos);
            curClickCell.moveToAndBack(lastPos);
            lastClickCell.moveToAndBack(pos);
            this.lastPos = cc.v2(-1, -1);
            return [this.changeModels];
        }
        else {
            this.lastPos = cc.v2(-1, -1);
            curClickCell.moveTo(lastPos, this.curTime);
            lastClickCell.moveTo(pos, this.curTime);
            var checkPoint = [pos, lastPos];
            this.curTime += v.ANITIME.TOUCH_MOVE;
            this.processCrush(checkPoint);
            return [this.changeModels, this.effectsQueue];
        }
    }
    // 消除
    processCrush(checkPoint) {
        let cycleCount = 0;
        while (checkPoint.length > 0) {
            let bombModels = [];
            if (cycleCount == 0 && checkPoint.length == 2) { //特殊消除
                let pos1 = checkPoint[0];
                let pos2 = checkPoint[1];
                let model1 = this.cells[pos1.x][pos1.y];
                let model2 = this.cells[pos2.x][pos2.y];
                if (model1.status == v.CELL_STATUS.BIRD || model2.status == v.CELL_STATUS.BIRD) {
                    if (model1.status == v.CELL_STATUS.BIRD) {
                        model1.type = model2.type;
                        bombModels.push(model1);
                    }
                    else {
                        model2.type = model1.type;
                        bombModels.push(model2);
                    }

                }
            }
            for (let pos of checkPoint) {
                if (!this.cells[pos.x][pos.y]) {
                    continue;
                }
                var [result, newCellStatus, newCellType] = this.checkPoint(pos.x, pos.y);
                if (result.length < 3) {
                    continue;
                }
                for (let res of result) {
                    var model = this.cells[res.x][res.y];
                    this.crushCell(res.x, res.y, false, cycleCount);
                    if (model.status != v.CELL_STATUS.COMMON) {
                        bombModels.push(model);
                    }
                }
                // 生成特殊格子
                this.createNewCell(pos, newCellStatus, newCellType);

            }
            this.processBomb(bombModels, cycleCount);
            this.curTime += v.ANITIME.DIE;
            checkPoint = this.down();
            cycleCount++;
        }
    }

    //生成新cell
    createNewCell(pos, status, type) {
        if (status == "") {
            return;
        }
        if (status == v.CELL_STATUS.BIRD) {
            type = v.CELL_TYPE.BIRD
        }
        let model = new CellModel();
        this.cells[pos.x][pos.y] = model
        model.init(type);
        model.setStartXY(pos.x, pos.y);
        model.setXY(pos.x, pos.y);
        model.setStatus(status);
        model.setVisible(0, false);
        model.setVisible(this.curTime, true);
        this.changeModels.push(model);
    }

    // 下落格子
    down() {
        let newCheckPoint = [];
        for (let j = v.GRID_COLUMNS; j >= 1; j--) {
            for (let i = v.GRID_ROWS; i >= 1; i--) {
                if (this.cells[i][j]) {
                    continue;
                }
                let curRow = i;
                // 计算动画
                for (let next = curRow; next >= 1; next--) {
                    if (!this.cells[next][j]) {
                        // 空格，不需要移动
                        continue;
                    }
                    newCheckPoint.push(this.cells[next][j]);
                    this.cells[curRow][j] = this.cells[next][j];
                    this.cells[next][j] = null;
                    this.cells[curRow][j].setXY(curRow, j);
                    this.cells[curRow][j].moveTo(cc.v2(curRow, j), this.curTime);
                    this.pushToChangeModels(this.cells[curRow][j]);
                    curRow--;
                }
                let count = 1;
                for (let next = curRow; next >= 1; next--) {
                    this.cells[next][j] = new CellModel();
                    this.cells[next][j].init(this.getRandomCellType());
                    this.cells[next][j].setStartXY(-count, j);
                    this.cells[next][j].setXY(-count, j);
                    this.cells[next][j].moveTo(cc.v2(next, j), this.curTime);
                    count++;
                    this.pushToChangeModels(this.cells[next][j]);
                    newCheckPoint.push(this.cells[next][j]);
                }
                break;
            }
        }
        this.curTime += v.ANITIME.TOUCH_MOVE + 0.3
        return newCheckPoint;
    }

    pushToChangeModels(model) {
        if (this.changeModels.indexOf(model) != -1) {
            return;
        }
        this.changeModels.push(model);
    }

    cleanCmd() {
        for (var i = 1; i <= v.GRID_ROWS; i++) {
            for (var j = 1; j <= v.GRID_COLUMNS; j++) {
                if (this.cells[i][j]) {
                    this.cells[i][j].cmd = [];
                }
            }
        }
    }

    exchangeCell(pos1, pos2) {
        let tmpModel = this.cells[pos1.x][pos1.y];
        this.cells[pos1.x][pos1.y] = this.cells[pos2.x][pos2.y];
        this.cells[pos1.x][pos1.y].x = pos1.x;
        this.cells[pos1.x][pos1.y].y = pos1.y;
        this.cells[pos2.x][pos2.y] = tmpModel;
        this.cells[pos2.x][pos2.y].x = pos2.x;
        this.cells[pos2.x][pos2.y].y = pos2.y;
    }

    // 设置种类
    // Todo 改成乱序算法
    setCellTypeNum(num) {
        this.cellTypeNum = num;
        this.cellCreateType = [];
        let createTypeList = this.cellCreateType;
        for (let i = 1; i <= v.CELL_BASENUM; i++) {
            createTypeList.push(i);
        }
        for (let i = 0; i < createTypeList.length; i++) {
            let index = Math.floor(Math.random() * (v.CELL_BASENUM - i)) + i;
            createTypeList[i], createTypeList[index] = createTypeList[index], createTypeList[i]
        }
    }

    // 随要生成一个类型
    getRandomCellType() {
        var index = Math.floor(Math.random() * this.cellTypeNum);
        return this.cellCreateType[index];
    }

    // TODO bombModels去重
    processBomb(bombModels, cycleCount) {
        while (bombModels.length > 0) {
            let newBombModel = [];
            let bombTime = v.ANITIME.BOMB_DELAY;
            for (let model of bombModels) {
                // 横向爆炸
                if (model.status == v.CELL_STATUS.LINE) {
                    for (let i = 1; i <= v.GRID_COLUMNS; i++) {
                        if (!this.cells[model.x][i]) {
                            continue;
                        }
                        if (this.cells[model.x][i].status != v.CELL_STATUS.COMMON) {
                            newBombModel.push(this.cells[model.x][i]);
                        }
                        this.crushCell(model.x, i, false, cycleCount);
                    }
                    // 添加横向爆炸效果
                    this.addRowBomb(this.curTime, cc.v2(model.x, model.y));
                }
                // 纵向爆炸
                else if (model.status == v.CELL_STATUS.COLUMN) {
                    for (let i = 1; i <= v.GRID_ROWS; i++) {
                        if (!this.cells[i][model.y]) {
                            continue;
                        }
                        if (this.cells[i][model.y].status != v.CELL_STATUS.COMMON) {
                            newBombModel.push(this.cells[i][model.y]);
                        }
                        this.crushCell(i, model.y, false, cycleCount);
                    }
                    this.addColBomb(this.curTime, cc.v2(model.x, model.y));
                }
                else if (model.status == v.CELL_STATUS.WRAP) {
                    let x = model.x;
                    let y = model.y;
                    for (let i = 1; i <= v.GRID_ROWS; i++) {
                        for (let j = 1; j <= v.GRID_COLUMNS; j++) {
                            let delta = Math.abs(x - i) + Math.abs(y - j);
                            if (this.cells[i][j] && delta <= 2) {
                                if (this.cells[i][j].status != v.CELL_STATUS.COMMON) {
                                    newBombModel.push(this.cells[i][j]);
                                }
                                this.crushCell(i, j, false, cycleCount);
                            }
                        }
                    }
                }
                else if (model.status == v.CELL_STATUS.BIRD) {
                    let crushType = model.type
                    if (bombTime < v.ANITIME.BOMB_BIRD_DELAY) {
                        bombTime = v.ANITIME.BOMB_BIRD_DELAY;
                    }
                    if (crushType == v.CELL_TYPE.BIRD) {
                        crushType = this.getRandomCellType();
                    }
                    for (let i = 1; i <= v.GRID_ROWS; i++) {
                        for (let j = 1; j <= v.GRID_COLUMNS; j++) {
                            if (this.cells[i][j] && this.cells[i][j].type == crushType) {
                                if (this.cells[i][j].status != v.CELL_STATUS.COMMON) {
                                    newBombModel.push(this.cells[i][j]);
                                }
                                this.crushCell(i, j, true, cycleCount);
                            }
                        }
                    }
                    //this.crushCell(model.x, model.y);
                }
            };
            if (bombModels.length > 0) {
                this.curTime += bombTime;
            }
            bombModels = newBombModel;
        }
    }
    /**
     * 
     * @param {开始播放的时间} playTime 
     * @param {*cell位置} pos 
     * @param {*第几次消除，用于播放音效} step 
     */
    addCrushEffect(playTime, pos, step) {
        this.effectsQueue.push({
            playTime,
            pos,
            action: "crush",
            step
        });
    }

    addRowBomb(playTime, pos) {
        this.effectsQueue.push({
            playTime,
            pos,
            action: "rowBomb"
        });
    }

    addColBomb(playTime, pos) {
        this.effectsQueue.push({
            playTime,
            pos,
            action: "colBomb"
        });
    }

    // cell消除逻辑
    crushCell(x, y, needShake, step) {
        let model = this.cells[x][y];
        this.pushToChangeModels(model);
        if (needShake) {
            model.toShake(this.curTime)
        }
        let shakeTime = needShake ? v.ANITIME.DIE_SHAKE : 0;
        model.toDie(this.curTime + shakeTime);
        this.addCrushEffect(this.curTime + shakeTime, cc.v2(model.x, model.y), step);
        this.cells[x][y] = null;
    }
}

module.exports = {
    GameModel,
};